using UnityEngine; // 引入Unity引擎的命名空间，用于游戏开发的基础功能  
using System.Collections; // 引入非泛型集合的命名空间，提供非泛型集合的基础功能  
using System; // 引入系统基础命名空间，提供基本的系统功能和类型  
using System.Threading; // 引入线程相关的命名空间，用于多线程编程  
using System.Collections.Generic; // 引入泛型集合的命名空间，提供泛型集合的基础功能  

using System.ComponentModel;
using System.Text.RegularExpressions;
using System.Text;
using System.Runtime.InteropServices; // 引入运行时互操作服务的命名空间，用于调用非托管代码（如DLL）  
using System.IO;


public class ComTrans : MonoBehaviour // 定义一个继承自MonoBehaviour的类，用于Unity中的游戏对象  
{
    // 编辑器模式下的模拟串口函数（不执行实际串口操作）  
#if UNITY_EDITOR
    // 模拟串口打开函数  
    int Serial_Open(string dev_path, int boad_rate, int flags, int min_btye)
    {
        return 0; // 在编辑器模式下总是返回0，表示打开失败  
    }

    // 模拟串口关闭函数  
    void Serial_Close()
    {
    }

    // 模拟串口发送数据函数  
    int Serial_SendData(byte[] com_data, int size)
    {
        return 0; // 在编辑器模式下总是返回0，表示发送失败  
    }

    // 模拟串口接收数据函数  
    int Serial_RecvData(byte[] com_data, int size)
    {
        return 0; // 在编辑器模式下总是返回0，表示接收失败  
    }

    // 模拟第二个串口的打开函数  
    int Serial_Open2()
    {
        return 0; // 在编辑器模式下总是返回0，表示打开失败  
    }

    // 模拟第二个串口的关闭函数  
    void Serial_Close2()
    {
    }

    // 模拟第二个串口发送数据函数  
    int Serial_SendData2(byte[] com_data, int size)
    {
        return 0; // 在编辑器模式下总是返回0，表示发送失败  
    }

    // 模拟第二个串口接收数据函数  
    int Serial_RecvData2(byte[] com_data, int size)
    {
        return 0; // 在编辑器模式下总是返回0，表示接收失败  
    }
#else
    // 非编辑器模式下，通过DllImport调用外部DLL中的串口函数  
    [DllImport("serialport")]  
    private static extern int Serial_Open(string dev_path, int boad_rate, int flags, int min_btye);  
  
    [DllImport("serialport")]  
    private static extern void Serial_Close();  
  
    [DllImport("serialport")]  
    private static extern int Serial_SendData(byte[] com_data, int size);  
  
    [DllImport("serialport")]  
    private static extern int Serial_RecvData(byte[] com_data, int size);  
  
    // 第二个串口的打开函数  
    [DllImport("serialport")]  
    private static extern int Serial_Open2();  
  
    // 第二个串口的关闭函数  
    [DllImport("serialport")]  
    private static extern void Serial_Close2();  
  
    // 第二个串口的发送数据函数  
    [DllImport("serialport")]  
    private static extern int Serial_SendData2(byte[] com_data, int size);  
  
    // 第二个串口的接收数据函数  
    [DllImport("serialport")]  
    private static extern int Serial_RecvData2(byte[] com_data, int size);  
#endif

    // 单例模式，确保整个应用中只有一个ComTrans实例  
    public static ComTrans Instance;

    public AudioSource audioSource;
    public AudioSource audioSource2;

    public AudioClip[] audioClip;


    // public TextMeshProUGUI textMeshProUGUI;
    // 接收数据相关  
    Thread dataRecvThread; // 接收数据的线程  
    const int MAX_CACHE_BUFF_LEN = 4096; // 最大缓存缓冲区长度  
    const int MIN_DATA_LEN = 10; // 最小包长度  

    // 接收到的数据包队列  
    public Queue<byte[]> recv_data_queue = new Queue<byte[]>();

    // 串口是否打开的标志  
    bool isOpen = false;

    // 发送数据相关  
    Thread dataSendThread; // 发送数据的线程  
    // 发包队列  
    public Queue<byte[]> send_data_queue = new Queue<byte[]>();

    // Awake在对象实例化时调用一次，用于初始化  
    private void Awake()
    {
        // 只保留在游戏中存在时间超过0秒的串口对象
        GameObject[] GaneTimeObject = GameObject.FindGameObjectsWithTag("GameManager");
        if (GaneTimeObject.Length > 1)
        {
            Destroy(gameObject);//存在相同对象，销毁对象
        }

        isOpen = false; // 初始化串口为关闭状态 

        if (Instance != null)
            Destroy(this);
        else
            Instance = this; // 设置单例实例  
    }

    public bool coinSerial;//投币信号
    private bool secSerial;//确认信号
    private bool setSerial;//设置信号
    private int horizontalL;
    private int horizontalR;
    private int verticalU;
    private int verticalD;
    public int gameOpen;

    // Start在对象首次激活时调用一次，用于开始执行  
    void Start()
    {
        audioSource.volume = (float)PlayerPrefs.GetInt("effectsValue", 100) / 100;
        DontDestroyOnLoad(gameObject); // 确保游戏对象在加载新场景时不被销毁  
        InvokeRepeating("CleanSerial", 5, 5);
        RunCom(1); // 根据版型决定调用时机，这里默认调用0  
    }

    #region 串口
    // 根据版型决定调用哪个串口和相关设置  
    public void RunCom(int type)
    {
        // 根据版型设置串口路径  
        string devPath = "";
        if (type == 0)
            devPath = "/dev/ttyS0"; // rk3288版型  
        else if (type == 1)
            devPath = "/dev/ttyS2"; // a33版型  
        else
            devPath = "/dev/ttyS1"; // 默认版型  

        // 打开主串口  
        int open_ret = Serial_Open(devPath, 9600, 0, 2);
        string str_open_ret = "open com ret:" + open_ret.ToString();
        // 开启线程
        if (open_ret > 0)
        {
            isOpen = true; // 串口打开成功  
            UnityEngine.Debug.Log(str_open_ret); // 打印打开结果  

            // 启动接收数据的线程  
            dataRecvThread = new Thread(new ThreadStart(DataRecvFunction));
            dataRecvThread.IsBackground = true; // 设置为后台线程，主线程结束时自动结束  
            dataRecvThread.Start();

            // 启动发送数据的线程  
            dataSendThread = new Thread(new ThreadStart(DataSendFunc));
            dataSendThread.IsBackground = true; // 设置为后台线程  
            dataSendThread.Start();
        }
        else
        {
            isOpen = false; // 串口打开失败  
            UnityEngine.Debug.Log(str_open_ret + "-111"); // 打印打开结果  
        }
    }

    // 处理接收到的串口数据（当前为空实现，需要在后续代码中添加处理逻辑）  
    void handle_com_data(byte[] dat, int dat_len)
    {
    }

    // 接收数据的线程函数  
    void DataRecvFunction()
    {
        while (true)
        {
            try
            {
                Byte[] buf = new Byte[256]; // 接收缓冲区  
                int len = Serial_RecvData(buf, 256); // 从串口接收数据  
                if (len > 0)
                {
                    Debug.Log($"receive len = {len}, buf.Length = {buf.Length}"); // 打印接收到的数据长度和缓冲区长度  
                }
                if (len > 0 && buf.Length > 0 && buf != null)
                {
                    //加入处理队列
                    lock (recv_data_queue)
                    {
                        recv_data_queue.Enqueue(buf);
                    }

                    //把收到的包变成字符串str_data1
                    string str_data1 = "";
                    for (int kk = 0; kk < len; kk++)
                    {
                        // str_data1 += buf[kk].ToString("X2");
                        str_data1 += Convert.ToChar(buf[kk]);
                    }
                    Debug.Log("serial rec:" + str_data1);
                    // textMeshProUGUI.text = str_data1;
                    Data_RE(str_data1);

                }

            }
            catch (Exception)
            {
                //print("System.Exception in serial.ReadLine: " + e.ToString());
            }

            Thread.Sleep(50);
        }
    }

    // 发送数据线程
    void DataSendFunc()
    {
        while (true)
        {
            if (send_data_queue.Count > 0)
            {
                lock (send_data_queue)
                {
                    byte[] com_data = send_data_queue.Dequeue();
                    if (isOpen)
                    {
                        int set_ret = Serial_SendData(com_data, com_data.Length);
                        //if (set_ret <= 0)
                        //DataHandler.Instance.OnError (1);
                    }
                }
            }

            Thread.Sleep(1);
        }
    }

    // 发送数据处理
    public void Send(byte[] buffer, bool check_resend = false)
    {
        string str_data1 = "";
        for (int i = 0; i < buffer.Length; i++)
        {
            str_data1 += buffer[i].ToString("X2");
        }

        if (str_data1.Length > 50)
        {
            str_data1 = str_data1.Substring(0, 9);
            str_data1 += "...";
        }

        Debug.Log("发往串口" + str_data1);

        lock (send_data_queue)
        {
            send_data_queue.Enqueue(buffer);
        }
    }

    #endregion

    // 退出关闭串口
    void OnApplicationQuit()
    {
        Serial_Close();
        Serial_Close2();
    }

    #region 数据处理
    // 获取设置信号
    public bool GetsetSerial()
    {
        bool temp = setSerial;
        setSerial = false;
        return temp;
    }
    // 获取确认信号
    public bool GetsecSerial()
    {
        bool temp = secSerial;
        secSerial = false;
        return temp;
    }
    // 获取投币信号
    public bool GetCoinSerial()
    {
        bool temp = coinSerial;
        coinSerial = false;
        return temp;
    }
    // 获取水平信号
    public int GetHorizontalL()
    {
        int temp = horizontalL;
        horizontalL = 0;
        return temp;
    }
    public int GetHorizontalR()
    {
        int temp = horizontalR;
        horizontalR = 0;
        return temp;
    }
    // 获取垂直信号
    public int GetVertucalU()
    {
        int temp = verticalU;
        verticalU = 0;
        return temp;
    }
    public int GetVertucalD()
    {
        int temp = verticalD;
        verticalD = 0;
        return temp;
    }

    // 数据处理
    public void Data_RE(string _data)
    {
        if (_data.Contains("Relay_OPEN"))
        {
            gameOpen = 1;
        }
        if (_data.Contains("Relay_CLOSE"))
        {
            gameOpen = 0;
        }
        switch (_data)
        {
            case "COIN":
                coinSerial = true;
                break;
            case "CSEC":
                secSerial = true;
                break;
            case "LSEL":
                horizontalL = -1;
                break;
            case "RSER":
                horizontalR = 1;
                break;
            case "USEU":
                verticalU = 1;
                break;
            case "DSED":
                verticalD = -1;
                break;
            case "TSET":
                setSerial = true;
                break;
                // case "Relay_OPEN":
                //     gameOpen = 1;
                //     break;
                // case "Relay_CLOSE":
                //     gameOpen = 0;
                //     break;

        }
        if (_data.Contains("Relay_OPEN"))
        {
            gameOpen = 1;
        }
        if (_data.Contains("Relay_CLOSE"))
        {
            gameOpen = 0;
        }
    }
    #endregion

    private void CleanSerial()
    {
        secSerial = false;
        coinSerial = false;
        setSerial = false;

    }

    public void ChangeGame()
    {
        // GAME
        byte[] datas = { 0x47, 0x41, 0x4D, 0x45 };
        Send(datas);
    }
    public void ChangeIdle()
    {
        // IDLE
        byte[] datas = { 0x49, 0x44, 0x4C, 0x45 };
        Send(datas);
    }
}
